package cn.lingyangwl.agile.infra.dict.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.lingyangwl.agile.infra.dict.mapper.DictItemMapper;
import cn.lingyangwl.agile.infra.dict.mapper.DictMapper;
import cn.lingyangwl.agile.infra.dict.model.DictResetMQ;
import cn.lingyangwl.agile.infra.dict.service.DictItemService;
import cn.lingyangwl.agile.infra.dict.service.DictMqService;
import cn.lingyangwl.agile.infra.module.dict.model.entity.Dict;
import cn.lingyangwl.agile.infra.module.dict.model.entity.DictItem;
import cn.lingyangwl.agile.infra.module.dict.model.rqrs.DictItemListReq;
import cn.lingyangwl.agile.model.BasePlusPO;
import cn.lingyangwl.agile.system.constants.CacheCons;
import cn.lingyangwl.framework.cache.core.service.CacheService;
import cn.lingyangwl.framework.tool.core.StringUtils;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author shenguangyang
 */
@Slf4j
@Service
@CacheConfig(cacheNames="dict")
public class DictItemServiceImpl extends ServiceImpl<DictItemMapper, DictItem>
        implements DictItemService {
    @Resource
    private DictItemMapper dictItemMapper;
    @Resource
    private DictMqService dictMqService;

    @Resource
    private DictMapper dictMapper;

    @Resource
    private CacheService cacheService;

    private static final Map<String, DictItem> LOCAL_DICT_CACHE = new ConcurrentHashMap<>();

    /**
     * key: dictType
     * value
     *  key: code
     *  value: 字典对象
     */
    private static final Map<String, Map<String, DictItem>> LOCAL_DICT_CACHE_MAP = new ConcurrentHashMap<>();

    @Override
    public List<DictItem> listByType(String dictType) {
        Object obj = cacheService.opsForValue().get(String.format(CacheCons.DICT, dictType));

        if (Objects.isNull(obj)) {
            LambdaQueryWrapper<DictItem> itemPOLambdaQueryWrapper = DictItem.lqw()
                    .eq(DictItem::getDictCode, dictType).orderByDesc(BasePlusPO::getUpdatedAt);
            List<DictItem> dictItemList = dictItemMapper.selectList(itemPOLambdaQueryWrapper);
            cacheService.opsForValue().set(String.format(CacheCons.DICT, dictType), JSON.toJSONString(dictItemList));
            return dictItemList;
        }
        return JSON.parseObject(String.valueOf(obj), new TypeReference<List<DictItem>>() {});
    }

    @Override
    public Map<String, Map<String, DictItem>> listByTypes(List<String> types) {
        Map<String, Map<String, DictItem>> resp = new HashMap<>(types.size());
        for (String type : types) {
            Map<String, DictItem> data = LOCAL_DICT_CACHE_MAP.get(type);
            if (data == null) {
                data = new HashMap<>();
                List<DictItem> dictItems = listByType(type);
                for (DictItem dictItem : dictItems) {
                    data.put(dictItem.getItemValue(), dictItem);
                }
                LOCAL_DICT_CACHE_MAP.put(type, data);
            }
            resp.put(type, data);
        }
        return resp;
    }

    @Override
    public DictItem getDictItem(String dictType, String itemValue) {
        String key = dictType + "_" + itemValue;
        DictItem data = LOCAL_DICT_CACHE.get(key);
        if (data != null) {
            return data;
        }
        List<DictItem> dictItemList = listByType(dictType);
        for (DictItem dictItem : dictItemList) {
            String cacheKey = dictType + "_" + dictItem.getItemValue();
            LOCAL_DICT_CACHE.put(cacheKey, dictItem);
        }
        // 如果不存在, 就赋值一个空, 防止每次都查db以及redis
        data = LOCAL_DICT_CACHE.get(key);
        if (data == null) {
            LOCAL_DICT_CACHE.put(key, new DictItem());
        }
        return data;
    }

    @Override
    public List<DictItem> listItem(DictItemListReq req) {
        LambdaQueryWrapper<DictItem> queryWrapper = DictItem.lqw()
                .eq(StringUtils.isNotEmpty(req.getDictCode()), DictItem::getDictCode, req.getDictCode())
                .eq(Objects.nonNull(req.getDictId()), DictItem::getDictId, req.getDictId())
                .like(StringUtils.isNotEmpty(req.getItemLabel()), DictItem::getItemLabel, req.getItemLabel())
                .eq(Objects.nonNull(req.getStatus()), DictItem::getStatus, req.getStatus())
                .orderByAsc(DictItem::getSortNo);
        return dictItemMapper.selectList(queryWrapper);
    }

    @Override
    public void deleteByIds(Long[] dictIds) {
        List<DictItem> dictItems = dictItemMapper.selectBatchIds(Arrays.asList(dictIds));
        dictItemMapper.deleteBatchIds(Arrays.stream(dictIds).collect(Collectors.toList()));

        List<String> dictCodes = dictItems.stream().map(DictItem::getDictCode).distinct().collect(Collectors.toList());
        for (String dictCode : dictCodes) {
            cacheService.opsForKey().delete(String.format(CacheCons.DICT, dictCode));
            dictMqService.resetLocalCache(new DictResetMQ(Collections.singleton(dictCode)));
        }
    }

    @Override
    public void deleteById(Long dictId) {
        DictItem dictItem = dictItemMapper.selectById(dictId);
        if (Objects.isNull(dictItem)) {
            return;
        }

        dictItemMapper.deleteById(dictId);
        cacheService.opsForKey().delete(String.format(CacheCons.DICT, dictItem.getDictCode()));
        dictMqService.resetLocalCache(new DictResetMQ(Collections.singleton(dictItem.getDictCode())));
    }

    @Override
    public boolean saveDictItem(DictItem dictItem) {
        Dict dict = dictMapper.selectById(dictItem.getDictId());
        if (Objects.isNull(dict)) {
            throw new BizException("选择的字典不存在");
        }
        dictItem.setDictCode(dict.getDictCode());
        dictItem.setIsDefault(StringUtils.firstNonEmpty(dictItem.getIsDefault(), "N"));

        cacheService.opsForKey().delete(String.format(CacheCons.DICT, dictItem.getDictCode()));
        return dictItemMapper.insert(dictItem) > 0;
    }

//    @FieldUniqueCheck(type = OperationTypeEnum.UPDATE)
    @Override
    public boolean updateDictItem(DictItem dictItem) {
        Dict dict = this.dictMapper.selectById(dictItem.getDictId());
        cacheService.opsForKey().delete(String.format(CacheCons.DICT, dict.getDictCode()));
        boolean ret = dictItemMapper.updateById(dictItem) > 0;
        dictMqService.resetLocalCache(new DictResetMQ(Collections.singleton(dictItem.getDictCode())));
        return ret;
    }

    @Override
    public void resetDictCache() {
        Set<String> keys = cacheService.opsForKey().keys(String.format(CacheCons.DICT, "*"));
        cacheService.opsForKey().delete(keys);

        dictMqService.resetLocalCache(new DictResetMQ());
    }

    @Override
    public void resetLocalDictCache(DictResetMQ req) {
        Set<String> types = req.getTypes();
        if (CollUtil.isEmpty(types)) {
            LOCAL_DICT_CACHE.clear();
            LOCAL_DICT_CACHE_MAP.clear();
        } else {
            types.forEach(e -> {
                LOCAL_DICT_CACHE.remove(e);
                LOCAL_DICT_CACHE_MAP.remove(e);
            });
        }

    }
}
